// Copyright (c) 2010 Eugene Korneychuk (zhech)
// This source is subject to the  MIT License.
// http://zhechajaxcontrols.codeplex.com/license.
// All other rights reserved.
Type.registerNamespace('AjaxControlToolkit');
AjaxControlToolkit.BreadCrumbBehavior = function(element) {
    AjaxControlToolkit.BreadCrumbBehavior.initializeBase(this, [element]);
    /**
     * Это фрейм, который отображается при наведении на элемент
     */
    this._hoverFrame = null;
    this._hoverFrameTop = null;
    this._hoverFrameBottom = null;
    this._hoverFrameLeft = null;
    this._hoverFrameRight = null;
    this._hoverFrameSeparator = null;
    this._dropArrowImageUrl = null;
    this._dropArrowWidth = 16;
    this._dropArrowBackgroundColor = "transparent";
    this._dropArrow = null;
    this._dropArrowImage = null;
    /**
     * Это фрейм панели
     */
    this._panelFrame = null;
    this._panelFrameTop = null;
    this._panelFrameBottom = null;
    this._panelFrameLeft = null;
    this._panelFrameRight = null;
    /**
     * Флаг того, открыта ли панель PopUp
     */
    this._isOpen = false;
    /**
     * Список элементов BreadCrumb
     */
    this._breadCrumbItems = [];
    this._currentPath = "";
    /**
     * Текущий путь к выбранному элементу
     */
    this._breadCrumbPath = [];
    /**
     * Path to the web service, or null if a page method
     */
    this._servicePath = null;
    /**
     * Name of the web method
     */
    this._serviceMethod = null;
    /**
     * Таймер, по которму необходимо вызвать Popup (автоматическое разварачивание списка)
     */
    this._timer = null;
    /**
     * Кеш полученных значений от веб-сервиса
     */
    this._cache = [];
    /**
     * Каскадная таблица стилей для панели
     */
    this._completionListCssClass = null;
    /**
     * Каскадная таблица стилей для панели при наведении
     */
    this._completionListHoverCssClass = null;
    /**
     * Каскадная таблица стилей для элемента списка
     */
    this._completionListItemCssClass = null;
    /**
     * Каскадная таблица стилей для элемента списка с фокусом
     */
    this._highlightedItemCssClass = null;
    /**
     * Подсветка границы выделения
     */
    this._highlightBorderColor = "#2353B2";
    /**
     * WebRequest object returned from WebServiceProxy.invoke
     */
    this._webRequest = null;
    this._completionInterval = 0;
    this._completionListElement = null;
    this._flyoutHasFocus = false;

    this._clPopupBehavior = null;
    this._clPopupBehaviorHiddenHandler = null;
    this._clBlurHandler = null;
    this._clMouseDownHandler = null;
    this._clMouseOverHandler = null;
    this._tickHandler = null;
    this._bodyClickHandler = null;
    this._currentBreadCrumb = null;
    this._currentContextKey = "";
    this._textColor = 'windowtext';
    this._textBackground = 'window';
    this._keyDownHandler = null;
    this._parent = null;
    this._selectIndex = -1;
    this._readOnly = false;
    this._initialized = false;
    this._minSelectableLevel = -1;
    this._doingPostBack = false;
    this._autoPostBack = false;
};

AjaxControlToolkit.BreadCrumbBehavior.prototype = {
    /**
    * Инициализация
    */
    initialize: function () {
        if (this._initialized) {
            return;
        }
        this._initialized = true;
        AjaxControlToolkit.BreadCrumbBehavior.callBaseMethod(this, 'initialize');
        var elt = this.get_element();
        var parent = elt.parentNode;
        this._parent = parent;

        $common.prepareHiddenElementForATDeviceUpdate();
        //this._clPopupBehaviorHiddenHandler = Function.createDelegate(this, this._onPopupHidden);
        this._tickHandler = Function.createDelegate(this, this._onTimerTick);
        this._clBlurHandler = Function.createDelegate(this, this._onListBlur);
        this._bodyClickHandler = Function.createDelegate(this, this._onListBlur);
        this._clMouseDownHandler = Function.createDelegate(this, this._onListMouseDown);
        this._clMouseOverHandler = Function.createDelegate(this, this._onListMouseOver);
        this._keyDownHandler = Function.createDelegate(this, this._onKeyDown);

        this._timer = new Sys.Timer();
        this.initializeTimer(this._timer);

        this._completionListElement = document.createElement('div');
        this._completionListElement.id = this.get_id() + '_completionListElem';
        //$(this._parent).append($(this._completionListElement));
        //document.body.appendChild(this._completionListElement);
        if (Sys.Browser.agent === Sys.Browser.Safari) {
            document.body.appendChild(this._completionListElement);
        } else {
            elt.parentNode.insertBefore(this._completionListElement, elt.nextSibling);
        }
        this.initializeCompletionList(this._completionListElement);
        var hoverArrowImageProperties = {};
        if (this._dropArrowImageUrl) {
            hoverArrowImageProperties.src = this._dropArrowImageUrl;
        }
        this._hoverFrame = $common.createElementFromTemplate({
            parent: parent,
            nameTable: this,
            name: "_hoverFrame",
            nodeName: "span",
            visible: false,
            id: this.get_id() + "_hF",
            properties: {
                id: this.get_id() + "_hF"
            },
            children: [{
                name: "_hoverFrameTop",
                id: "_hoverFrameTop",
                nodeName: "div",
                cssClasses: ["ajax__breadcrumb_frame_line"]
            },
            {
                name: "_hoverFrameRight",
                id: "_hoverFrameRight",
                nodeName: "div",
                cssClasses: ["ajax__breadcrumb_frame_line"]
            },
            {
                name: "_hoverFrameBottom",
                id: "_hoverFrameBottom",
                nodeName: "div",
                cssClasses: ["ajax__breadcrumb_frame_line"]
            },
            {
                name: "_hoverFrameLeft",
                id: "_hoverFrameLeft",
                nodeName: "div",
                cssClasses: ["ajax__breadcrumb_frame_line"]
            },
            {
                name: "_hoverFrameSeparator",
                id: "_hoverFrameSeparator",
                nodeName: "div",
                cssClasses: ["ajax__breadcrumb_frame_line"]
            },
            {
                name: "_dropArrow",
                nodeName: "div",
                cssClasses: (!this._dropArrowImageUrl) ? ["ajax__breadcrumb_arrow_image"] : ["ajax__breadcrumb_arrow"],
                id: this.get_id() + "_dA",
                properties: {
                    id: this.get_id() + "_dA",
                    style: {
                        width: this._dropArrowWidth,
                        backgroundColor: this._dropArrowBackgroundColor
                    }
                },
                children: [{
                    name: "_dropArrowWrapper",
                    nodeName: "div",
                    visible: !!this._dropArrowImageUrl,
                    cssClasses: ["ajax__breadcrumb_arrow_wrapper"],
                    children: [{
                        name: "_dropArrowImage",
                        nodeName: "img",
                        properties: hoverArrowImageProperties
                    }]
                }]
            }]
        },
        this._parent);
        this._hoverFrame._pathParent = this;
        var clickData = {
            path: this,
            elem: null
        };
        $("#" + this.get_id() + "_hF").bind("click", clickData, this._breadCrumbRemove_onclick);
        $("#" + this.get_id() + "_dA").bind("click", clickData, this._breadCrumbPopulate_onclick);

        this._panelFrame = $common.createElementFromTemplate({
            parent: parent,
            nameTable: this,
            name: "_panelFrame",
            nodeName: "span",
            visible: false,
            properties: {
                id: this.get_id() + "_pF"
            },
            children: [{
                name: "_panelFrameTop",
                nodeName: "div",
                cssClasses: ["ajax__breadcrumb_frame_line"],
                properties: {
                    id: this.get_id() + "_pFT"
                }
            },
            {
                name: "_panelFrameRight",
                nodeName: "div",
                cssClasses: ["ajax__breadcrumb_frame_line"],
                properties: {
                    id: this.get_id() + "_pFR"
                }
            },
            {
                name: "_panelFrameBottom",
                nodeName: "div",
                cssClasses: ["ajax__breadcrumb_frame_line"],
                properties: {
                    id: this.get_id() + "_pFB"
                }
            },
            {
                name: "_panelFrameLeft",
                nodeName: "div",
                cssClasses: ["ajax__breadcrumb_frame_line"],
                properties: {
                    id: this.get_id() + "_pFL"
                }
            }]
        });
        this.initializePanel(this._parent);
        this._breadCrumbProcessPath();
        this._currentPath = this._getSerializedPath();
        this.saveClientState();
    },
    initializeTimer: function (timer) {
        /// <summary>
        /// Initializes the timer
        /// </summary>
        /// <param name="timer" type="Sys.UI.Timer" DomElement="false" mayBeNull="false" />
        /// <returns />
        timer.set_interval(this._completionInterval);
        timer.add_tick(this._tickHandler);
    },
    initializeCompletionList: function (element) {
        /// <summary>
        /// Initializes the autocomplete list element
        /// </summary>
        /// <param name="element" type="Sys.UI.DomElement" DomElement="true" mayBeNull="false" />
        /// <returns />
        $common.setVisible(element, false);
        if (this._completionListCssClass) {
            $(element).addClass(this._completionListCssClass);

        } else {
            var completionListStyle = element.style;
            completionListStyle.textAlign = 'left';
            completionListStyle.visibility = 'hidden';
            completionListStyle.cursor = 'default';
            completionListStyle.listStyle = 'none';
            completionListStyle.padding = '0px';
            completionListStyle.margin = '0px! important';
            if (Sys.Browser.agent === Sys.Browser.Safari) {
                completionListStyle.border = 'solid 1px gray';
                completionListStyle.backgroundColor = 'white';
                completionListStyle.color = 'black';
            } else {
                completionListStyle.border = 'solid 1px buttonshadow';
                completionListStyle.backgroundColor = this._textBackground;
                completionListStyle.color = this._textColor;
            }
        }
        element._pathParent = this;
        $(element).addClass("ajax__breadcrumb_path");
        $(element).click(this._onListMouseDown);
        //$addHandler(element, "mousedown", this._clMouseDownHandler);
        $addHandler(element, "mouseover", this._clMouseOverHandler);
        $addHandler(element, "blur", this._clBlurHandler);
        $addHandler(document.body, 'click', this._bodyClickHandler);
    },
    initializePanel: function (pnl) {
        if (this._completionListCssClass) {
            $(pnl).addClass(this._completionListCssClass);
        }
        if (this._completionListHoverCssClass) {
            if (!jQuery.browser.msie) {
                var pnlID = $(pnl).attr('id');
                if (pnlID) {
                    $("#" + pnlID + " > *, #" + pnlID + ", #" + this._hoverFrame.id).die("mouseenter").die("mouseleave");
                    $("#" + pnlID + " > *, #" + pnlID + ", #" + this._hoverFrame.id).live("mouseenter", {
                        hoverClass: this._completionListHoverCssClass,
                        panel: pnl
                    },
                    function (e) {
                        $(e.data.panel).addClass(e.data.hoverClass);
                    });
                    $("#" + pnlID + ">*, #" + pnlID + ", #" + this._hoverFrame.id).live("mouseleave", {
                        hoverClass: this._completionListHoverCssClass,
                        panel: pnl
                    },
                    function (e) {
                        $(e.data.panel).removeClass(e.data.hoverClass);
                    });
                }
            }
        }
    },
    /**
    * Утилизация
    */
    dispose: function () {
        if (this._isOpen) {
            this._hideCompletionList();
            this._wasAdjusted = false;
            this._isOpen = false;
        }
        if (this._hoverFrame) {
            $(this._hoverFrame).unbind();
            $(this._dropArrow).unbind();
            this._hoverFrameTop = null;
            this._hoverFrameBottom = null;
            this._hoverFrameLeft = null;
            this._hoverFrameRight = null;
            this._hoverFrameSeparator = null;
            this._dropArrowImageUrl = null;
            this._dropArrow = null;
            this._dropArrowImage = null;
            $common.removeElement(this._hoverFrame);
            this._hoverFrame = null;
        }
        if (this._panelFrame) {
            $(this._panelFrame).unbind();
            this._panelFrame = null;
            this._panelFrameTop = null;
            this._panelFrameBottom = null;
            this._panelFrameLeft = null;
            this._panelFrameRight = null;
            $common.removeElement(this._panelFrame);
        }
        $(this.get_element()).text(this._breadCrumbItems[0]._value);
        this._breadCrumbRemove(-1, false, false);
        this._breadCrumbItems = null;
        this._breadCrumbPath[0] = null;
        this._breadCrumbPath.length = 0;
        this._breadCrumbPath = null;
        this._cache = null;
        if (this._timer) {
            this._timer.dispose();
            this._timer = null;
        }
        this._clPopupBehaviorHiddenHandler = null;
        this._tickHandler = null;
        this._clBlurHandler = null;
        if (this._bodyClickHandler) {
            $removeHandler(document.body, 'click', this._clBlurHandler);
            this._bodyClickHandler = null;
        }
        this._clMouseDownHandler = null;
        this._clMouseOverHandler = null;
        this._keyDownHandler = null;
        $("#" + this._parent.id + ">*").unbind();
        if (this._webRequest) {
            this._webRequest.get_executor().abort();
            this._webRequest = null;
        }
        AjaxControlToolkit.BreadCrumbBehavior.callBaseMethod(this, 'dispose');
    },

    /**
    * Воссоздает путь изначально
    */
    _breadCrumbProcessPath: function () {
        if (!this._initialized) {
            return;
        }
        var elt = $(this.get_element());
        var text = elt.text();
        if (elt.size() > 0) {
            $(elt).text("").html("").width(0).height(0).css("font-size", 0);
        }
        if (this._breadCrumbPath && (this._breadCrumbPath.length > 0)) {
            var key = null;
            var value = null;
            this._breadCrumbRemove(-1, false, false);
            for (i = 0; i < this._breadCrumbPath.length; i++) {
                try {
                    var pair = Sys.Serialization.JavaScriptSerializer.deserialize('(' + this._breadCrumbPath[i] + ')');
                    if (pair && pair.First) {
                        // Use the text and value pair returned from the web service
                        key = pair.First;
                        value = pair.Second;
                    } else {
                        // If the web service only returned a regular string, use it for
                        // both the text and the value
                        key = pair;
                        value = pair;
                    }
                } catch (ex) {
                    key = this._breadCrumbPath[i];
                    value = this._breadCrumbPath[i];
                }
                this._breadCrumbAppend(key, value, false);
            }
        } else {
            this._breadCrumbAppend("", text, true);
        }
        this._currentBreadCrumb = this._breadCrumbItems[this._breadCrumbItems.length - 1];
        //$(this._currentBreadCrumb).children("div:eq(1)").hide();
    },
    /**
    * Функиция добавляет breadCrumb в конец списка
    * @param {Object} key
    * @param {Object} value
    */
    _breadCrumbAppend: function (key, value, raiseChanged) {
        var index = this._breadCrumbItems.length;
        //$(this._currentBreadCrumb).children("div:eq(1)").show();
        this._breadCrumbItems[index] = this._breadCrumbCreateElement(key, value, index);

        this._currentBreadCrumb = this._breadCrumbItems[index];
        var pair = {
            First: this._breadCrumbItems[index]._key,
            Second: this._breadCrumbItems[index]._value
        };
        this._breadCrumbPath[index] = Sys.Serialization.JavaScriptSerializer.serialize(pair);
        if ((raiseChanged === null) || (raiseChanged === true)) {
            this._currentPath = this._getSerializedPath();
            this.saveClientState();
            this.raisePropertyChanged('currentPath');
        }
    },
    /**
    * Создает элемент BreadCrumb с заданным индексом в пути
    * @param {Object} key
    * @param {Object} value
    * @param {Object} index
    */
    _breadCrumbCreateElement: function (key, value, index) {
        if (!index || (index < 0)) {
            index = this._breadCrumbItems.length;
        }
        var hoverArrowImageProperties = {};
        if (this._dropArrowImageUrl) {
            hoverArrowImageProperties.src = this._dropArrowImageUrl;
        }
        var id = this.get_id();
        var wrapperID = id + "_dW_" + index;
        var wrapper = $common.createElementFromTemplate({
            nodeName: "div",
            cssClasses: (this._completionListItemCssClass ? [this._completionListItemCssClass, "ajax__breadcrumb_menu_path"] : ["ajax__breadcrumb_menu_path"]),
            properties: {
                id: id + "_dW_" + index,
                _index: index,
                _key: key,
                _value: value
            }
        },
        this._parent);
        var wrapperTextHolder = $common.createElementFromTemplate({
            nodeName: "div",
            cssClasses: ["ajax__breadcrumb_menu_path"],
            properties: {
                id: id + "_dTD0_" + index,
                innerHTML: value
            }
        },
        wrapper);
        var wrapperArrow = $common.createElementFromTemplate({
            nodeName: "div",
            cssClasses: (!this._dropArrowImageUrl) ? ["ajax__breadcrumb_menu_path", "ajax__breadcrumb_arrow_image"] : ["ajax__breadcrumb_menu_path", "ajax__breadcrumb_arrow"],
            properties: {
                id: id + "_dTD1_" + index,
                innerHTML: "&nbsp;",
                style: {
                    width: this._dropArrowWidth,
                    backgroundPosition: "center"
                }
            }
        },
        wrapper);
        wrapper._pathParent = this;
        wrapper._isOver = false;
        $(wrapperArrow).width(this._dropArrowWidth);

        if (!this._readOnly) {
            var hoverData = {
                breadCrumb: this,
                breadCrumbItem: wrapper,
                hoverFrame: this._hoverFrame
            };
            if (index >= this._minSelectableLevel) {
                $(wrapper).hover(this._breadCrumb_OnHover, this._breadCrumb_OnUnhover);
                var clickData = {
                    path: this,
                    elem: wrapper
                };
                $("#" + this.get_id() + "_dTD0_" + index).bind("click", clickData, this._breadCrumbRemove_onclick);
                $("#" + this.get_id() + "_dTD1_" + index).bind("click", clickData, this._breadCrumbPopulate_onclick);
                $(wrapper).focusin(this._breadCrumb_OnHover);
                $(wrapper).focusout(this._breadCrumb_OnUnhover);
            }
        }
        return wrapper;
    },
    /**
    * Функиция удаляет все последующие за breadCrumb узлы списка
    */
    _breadCrumbRemove: function (startIndex, clearPath, raiseChanged) {
        if (this._breadCrumbItems !== null) {
            var removeFirst = false;
            if (startIndex && (startIndex < 0)) {
                removeFirst = true;
                startIndex = -1;
            }
            if (startIndex === null) {
                startIndex = 0;
            }
            if (clearPath === null) {
                clearPath = true;
            }
            if (startIndex < this._breadCrumbItems.length - 1) {
                var pathLength = this._breadCrumbItems.length;
                for (i = pathLength - 1; i > startIndex; i--) {
                    if (clearPath) {
                        this._breadCrumbPath[i] = null;
                        this._breadCrumbPath.length = i;
                    }
                    $(this._breadCrumbItems[i]).trigger("mouseleave");
                    this._breadCrumbDisposeElement(this._breadCrumbItems[i]);
                    this._breadCrumbItems[i] = null;
                    this._breadCrumbItems.length = i;
                }
                if (startIndex >= 0) {
                    this._currentBreadCrumb = this._breadCrumbItems[startIndex];
                }
                if ((raiseChanged === null) || (raiseChanged === true)) {
                    this._currentPath = this._getSerializedPath();
                    this.saveClientState();
                    this.raisePropertyChanged('currentPath');
                }
            }
        }
    },
    /**
    * Освобождает элемент BreadCrumb
    * @param {Object} breadCrumb
    */
    _breadCrumbDisposeElement: function (breadCrumb) {
        if (breadCrumb) {
            breadCrumb._isOver = false;
            breadCrumb._pathParent = null;
            $(breadCrumb).unbind().remove();
        }
    },
    _breadCrumb_OnHover: function (sender, e) {
        sender.preventDefault();
        if (!this._isOver && !this._pathParent._isOpen) {
            this._isOver = true;
            if (this._pathParent._highlightedItemCssClass) {
                $(this).addClass(this._pathParent._highlightedItemCssClass);
            }
            this._pathParent._currentBreadCrumb = this;
            var bounds = $common.getBounds(this);
            $common.setLocation(this._pathParent._hoverFrame, {
                x: 0,
                y: 0
            });
            $common.setVisible(this._pathParent._hoverFrame, true);
            var offset = $common.getLocation(this._pathParent._hoverFrame);
            $common.setVisible(this._pathParent._hoverFrame, false);
            bounds.x -= offset.x;
            bounds.y -= offset.y;

            $common.setBounds(this._pathParent._hoverFrameTop, {
                x: bounds.x,
                y: bounds.y,
                width: bounds.width,
                height: 1
            });
            $common.setBounds(this._pathParent._hoverFrameBottom, {
                x: bounds.x,
                y: bounds.y + bounds.height - 1,
                width: bounds.width,
                height: 1
            });
            $common.setBounds(this._pathParent._hoverFrameLeft, {
                x: bounds.x,
                y: bounds.y,
                width: 1,
                height: bounds.height
            });
            $common.setBounds(this._pathParent._hoverFrameRight, {
                x: bounds.x + bounds.width - 1,
                y: bounds.y,
                width: 1,
                height: bounds.height
            });
            $common.setBounds(this._pathParent._hoverFrameSeparator, {
                x: bounds.x + bounds.width - this._pathParent._dropArrowWidth - 1,
                y: bounds.y,
                width: 1,
                height: bounds.height
            });
            $common.setBounds(this._pathParent._dropArrow, {
                x: bounds.x + bounds.width - this._pathParent._dropArrowWidth - 1,
                y: bounds.y,
                width: this._pathParent._dropArrowWidth,
                height: bounds.height
            });
            this._pathParent._hoverFrameTop.style.backgroundColor = this._pathParent._highlightBorderColor;
            this._pathParent._hoverFrameRight.style.backgroundColor = this._pathParent._highlightBorderColor;
            this._pathParent._hoverFrameBottom.style.backgroundColor = this._pathParent._highlightBorderColor;
            this._pathParent._hoverFrameLeft.style.backgroundColor = this._pathParent._highlightBorderColor;
            this._pathParent._hoverFrameSeparator.style.backgroundColor = this._pathParent._highlightBorderColor;
            this._highlighted = true;
            this._pathParent._hoverFrame._target = this;
            $(this._pathParent._hoverFrame).hover(function () {
                $(this._target).trigger("mouseenter", this._target);
            },
            function () {
                $(this._target).trigger("mouseleave", this._target);
            });
            $common.setVisible(this._pathParent._hoverFrameSeparator, true);
            $common.setVisible(this._pathParent._hoverFrame, true);
        }
    },
    _breadCrumb_OnUnhover: function (sender, e) {
        if (this._pathParent._highlightedItemCssClass) {
            $(this).removeClass(this._pathParent._highlightedItemCssClass);
        }
        if (this._isOver && !this._pathParent._isOpen) {
            this._isOver = false;
            var canHideFrame = true;
            for (i = 0; i < this._pathParent._breadCrumbItems; i++) {
                if ((i != this._index) && this._pathParent._breadCrumbItems[i]._isOver) {
                    canHideFrame = false;
                    break;
                }
            }
            if (canHideFrame) {
                $common.setVisible(this._pathParent._hoverFrame, false);
            }
        }
    },
    _breadCrumbRemove_onclick: function (e) {
        var path = e.data.path;
        var elem = e.data.elem;
        if (!elem) {
            elem = path._currentBreadCrumb;
        }
        if (path) {
            if (path._readOnly) {
                return;
            }
            e.stopPropagation();
            if (path._isOpen) {
                path._hideCompletionList();
                $(path._currentBreadCrumb).trigger("mouseleave");
            }
            if (path._currentBreadCrumb !== elem) {
                $(elem).trigger("mouseenter");
            } else {
                path._breadCrumbRemove(elem._index, true, true);
                path.raiseOnItemSelected(new AjaxControlToolkit.BreadCrumbItemEventArgs(elem, elem._value, elem._key));
            }
        }
    },
    _breadCrumbPopulate_onclick: function (e) {
        var path = e.data.path;
        var elem = e.data.elem;
        var reshow = false;
        if (!elem) {
            elem = path._currentBreadCrumb;
            reshow = true;
        }
        if (path) {
            if (path._readOnly) {
                return;
            }
            e.stopPropagation();
            if (path._isOpen) {
                if (!reshow) {
                    $(path._currentBreadCrumb).trigger("mouseleave");
                    path.hideCompletionList();
                    if (elem !== path._currentBreadCrumb) {
                        $(elem).trigger("mouseenter");
                        path._timer.set_enabled(true);
                    }
                }
                else {
                    path.hideCompletionList();
                }
            }
            else {
                path._isOpen = false;
                path._timer.set_enabled(true);
            }
        }
    },
    _handleScroll: function (element, index) {
        /// <summary>
        /// Method to determine if an item is in view or not
        /// </summary>
        /// <returns />
        /// <param name="element" type="Sys.UI.DomElement" DomElement="true" mayBeNull="false" />
        /// <param name="index" type="Number" DomElement="false" mayBeNull="true" />
        try {
            var flyout = this._completionListElement;
            var elemBounds = $common.getBounds(element);
            var numItems = this._completionListElement.childNodes.length;
            if (((elemBounds.height * index) - (flyout.clientHeight + flyout.scrollTop)) >= 0) {
                // you need to scroll down
                flyout.scrollTop += (((elemBounds.height * index) - (flyout.clientHeight + flyout.scrollTop)) + elemBounds.height);
            }
            if (((elemBounds.height * (numItems - (index + 1))) - (flyout.scrollHeight - flyout.scrollTop)) >= 0) {
                // you need to scroll up
                flyout.scrollTop -= (((elemBounds.height * (numItems - (index + 1))) - (flyout.scrollHeight - flyout.scrollTop)) + elemBounds.height);
            }

            if (flyout.scrollTop % elemBounds.height !== 0) {
                if (((elemBounds.height * (index + 1)) - (flyout.clientHeight + flyout.scrollTop)) >= 0) {
                    // an element is partially displayed at the bottom
                    flyout.scrollTop -= (flyout.scrollTop % elemBounds.height);
                } else { // an element is partially displayed on the top 
                    flyout.scrollTop += (elemBounds.height - (flyout.scrollTop % elemBounds.height));
                }
            }
        } catch (exc) {
        }
    },
    _onKeyDown: function (ev) {
        /// <summary>
        /// Handler for the textbox keydown event.
        /// </summary>
        /// <param name="ev" type="Sys.UI.DomEvent" DomElement="false" mayBeNull="false" />
        /// <returns />      
        var bc = ev.data.breadCrumbPath;
        var cc = ev.data.breadCrumb;
        bc._timer.set_enabled(false);
        // the last key press occured so we need to "reset" the timer.
        var k = ev.which;
        if (k == Sys.UI.Key.esc) {
            bc._hideCompletionList();
            ev.preventDefault();
        } else if (k == Sys.UI.Key.up) {
            if (bc._selectIndex > 0) {
                bc._selectIndex--;
                bc._handleScroll(bc._completionListElement.childNodes[bc._selectIndex], bc._selectIndex);
                bc._highlightItem(bc._completionListElement.childNodes[bc._selectIndex]);
                ev.stopPropagation();
                ev.preventDefault();
            }
        } else if (k == Sys.UI.Key.down) {
            if (bc._selectIndex < (bc._completionListElement.childNodes.length - 1)) {
                bc._selectIndex++;
                bc._handleScroll(bc._completionListElement.childNodes[bc._selectIndex], bc._selectIndex);
                bc._highlightItem(bc._completionListElement.childNodes[bc._selectIndex]);
                ev.stopPropagation();
                ev.preventDefault();
            }
        } else if (k == Sys.UI.Key.enter) {
            if (bc._selectIndex !== -1) {
                bc._breadCrumbRemove(cc._index, true, false);
                bc._breadCrumbAppend(bc._completionListElement.childNodes[bc._selectIndex]._key, $(bc._completionListElement.childNodes[bc._selectIndex]).text(), true);
                path.raiseOnItemSelected(new AjaxControlToolkit.BreadCrumbItemEventArgs(bc._currentBreadCrumb, bc._currentBreadCrumb._value, bc._currentBreadCrumb._key));
                bc._hideCompletionList();
                ev.preventDefault();
            } else {
                // close the popup
                bc.hideCompletionList();
            }
        } else if (k == Sys.UI.Key.tab) {
            bc._hideCompletionList();
            if (bc._selectIndex !== -1) {
                bc._breadCrumbRemove(cc._index, true, false);
                bc._breadCrumbAppend(bc._completionListElement.childNodes[bc._selectIndex]._key, $(bc._completionListElement.childNodes[bc._selectIndex]).text(), true);
                path.raiseOnItemSelected(new AjaxControlToolkit.BreadCrumbItemEventArgs(bc._currentBreadCrumb, bc._currentBreadCrumb._value, bc._currentBreadCrumb._key));
            }
        }
    },

    showCompletionList: function () {
        /// <summary>
        /// Show the completion list popup
        /// </summary>
        /// <remarks>
        /// If you cancel the showing event, you should still call
        /// showCompletionList to ensure consistency of the internal state
        /// </remarks>
        if (!this._isOpen) {
            this._isOpen = true;
            var isBottomPanel = true;
            var boundsBC = $common.getBounds(this._currentBreadCrumb);
            $common.setLocation(this._panelFrame, {
                x: 0,
                y: 0
            });
            $common.setVisible(this._panelFrame, true);
            var offset = $common.getLocation(this._panelFrame);
            $common.setVisible(this._panelFrame, false);
            boundsBC.x -= offset.x;
            boundsBC.y -= offset.y;
            var viewportBounds = $common.getClientBounds();
            var boundsP = $common.getBounds(this._completionListElement);
            boundsP.height = 0;
            boundsP.width = 0;
            $common.setVisible(this._completionListElement, true);
            //specify start bounds height
            if (this._completionListElement.childNodes.length > 0) {
                $(this._completionListElement).width(1);
                var numItems = this._completionListElement.childNodes.length;
                var itempHeight = $(this._completionListElement.childNodes[0]).outerHeight();
                var itemWidth = $(this._completionListElement).outerWidth();
                boundsP.width = itemWidth;
                boundsP.height = itempHeight * numItems + 1;
                if (this._completionListElement.scrollWidth) {
                    if (this._completionListElement.scrollWidth > 0) {
                        boundsP.width = boundsP.width + this._completionListElement.scrollWidth;
                    }
                }
            }
            $common.setVisible(this._completionListElement, false);
            var bounds = {
                x: boundsBC.x + 1,
                y: boundsBC.y + boundsBC.height,
                width: boundsP.width,
                height: boundsP.height
            };
            if (bounds.width < boundsBC.width - 2) {
                bounds.width = boundsBC.width - 2;
            }
            var topHeight = boundsBC.y;
            var bottomHeight = viewportBounds.y + viewportBounds.height - boundsBC.y - boundsBC.height;
            if ((bottomHeight >= topHeight) && (bottomHeight < bounds.height)) {
                bounds.height = bottomHeight - 4;
                bounds.width = bounds.width + 16;
            } else {
                if (bottomHeight < topHeight) {
                    isBottomPanel = false;
                    if (topHeight < bounds.height) {
                        bounds.height = (topHeight - viewportBounds.y) || 0;
                        bounds.y = boundsBC.y - topHeight + viewportBounds.y + offset.y + 1;
                        bounds.width = bounds.width + 16;
                    } else {
                        bounds.y = boundsBC.y - bounds.height + viewportBounds.y + offset.y + 1;
                        bounds.width = bounds.width;
                    }
                }
            }
            if (isBottomPanel) {
                $common.setBounds(this._panelFrameTop, {
                    x: bounds.x + boundsBC.width - 1,
                    y: bounds.y - 1,
                    width: bounds.width - boundsBC.width + 2,
                    height: 1
                });
                $common.setBounds(this._panelFrameBottom, {
                    x: bounds.x - 1,
                    y: bounds.y + bounds.height + 2,
                    width: bounds.width + 2,
                    height: 1
                });
                $common.setBounds(this._panelFrameLeft, {
                    x: bounds.x - 1,
                    y: bounds.y,
                    width: 1,
                    height: bounds.height + 2
                });
                $common.setBounds(this._panelFrameRight, {
                    x: bounds.x + bounds.width,
                    y: bounds.y,
                    width: 1,
                    height: bounds.height + 2
                });
                $common.setVisible(this._hoverFrameBottom, false);
            } else {
                $common.setBounds(this._panelFrameTop, {
                    x: bounds.x - 1,
                    y: bounds.y - 1,
                    width: bounds.width + 2,
                    height: 1
                });
                $common.setBounds(this._panelFrameBottom, {
                    x: bounds.x + boundsBC.width - 1,
                    y: bounds.y + bounds.height,
                    width: ((bounds.width - boundsBC.width - 1) < 0 ? 0 : (bounds.width - boundsBC.width + 1)),
                    height: 1
                });
                $common.setBounds(this._panelFrameLeft, {
                    x: bounds.x - 1,
                    y: bounds.y - 1,
                    width: 1,
                    height: bounds.height + 2
                });
                $common.setBounds(this._panelFrameRight, {
                    x: bounds.x + bounds.width,
                    y: bounds.y - 1,
                    width: 1,
                    height: bounds.height + 2
                });
                $common.setVisible(this._hoverFrameTop, false);
            }
            $common.setBounds(this._completionListElement, bounds);
            $common.setVisible(this._hoverFrameSeparator, false);
            this._panelFrameTop.style.backgroundColor = this._highlightBorderColor;
            this._panelFrameRight.style.backgroundColor = this._highlightBorderColor;
            this._panelFrameBottom.style.backgroundColor = this._highlightBorderColor;
            this._panelFrameLeft.style.backgroundColor = this._highlightBorderColor;
            $common.setVisible(this._panelFrame, true);
            $common.setVisible(this._completionListElement, true);
            //$(this).focus();
            //$("#" + this._completionListElement.id + ">*,#" + this._completionListElement.id)
            $("*").live("keyup", {
                breadCrumbPath: this,
                breadCrumb: this._currentBreadCrumb
            },
            this._keyDownHandler);
        }
    },
    hideCompletionList: function () {
        /// <summary>
        /// Hide the completion list popup
        /// </summary>
        /// <remarks>
        /// If you cancel the hiding event, you should still
        /// call hideCompletionList to ensure consistency of the internal
        /// state
        /// </remarks>
        if (this._isOpen) {
            this._isOpen = false;
            $common.setVisible(this._panelFrame, false);
            $common.setVisible(this._completionListElement, false);
            $common.setVisible(this._hoverFrameSeparator, true);
            $common.setVisible(this._hoverFrameBottom, true);
            $common.setVisible(this._hoverFrameTop, true);
            $("*").die("keyup");
            $(this._currentBreadCrumb).trigger("mouseleave");
        }
    },
    _hideCompletionList: function () {
        // Actually hide the popup
        this.hideCompletionList();
    },
    _getCurrentBreadCrumbKey: function () {
        var res = "";
        if (this._currentBreadCrumb !== null) {
            res = $(this._currentBreadCrumb).attr("_key");
        }
        return res;
    },
    _onPanelMouseOver: function (ev) {
        /// <summary>
        /// Handler for the mouseover event on the autocomplete flyout.
        /// </summary>
        /// <param name="ev" type="Sys.UI.DomEvent" DomElement="false" mayBeNull="false" />
        /// <returns />      
        var item = ev.target;
        if (this._completionListHoverCssClass) {
            $(this._parent).addClass(this._completionListHoverCssClass);
        }
        if ((item !== this._parent) && (item.tagName == "TABLE")) {
            var frameWasShown = false;
            var children = this._breadCrumbItems;
            // make sure the selected index is updated
            for (var i = 0; i < children.length; ++i) {
                if (item === children[i]) {
                    if (item !== this._currentBreadCrumb) {
                        this._breadCrumpItemUnighLight(this._currentBreadCrumb, false);
                        this._breadCrumpItemHighLight(item);
                        this._currentBreadCrumb = item;
                    }
                    frameWasShown = true;
                    break;
                }
            }
            if (!frameWasShown) {
                this._breadCrumpItemUnighLight(this._currentBreadCrumb, true);
            }
        }
    },
    _onPanelBlur: function (ev) {
        if (this._completionListHoverCssClass) {
            $(this._parent).removeClass(this._completionListHoverCssClass);
        }
        this._breadCrumpItemUnighLight(this._currentBreadCrumb, true);
    },
    _breadCrumpItemUnighLight: function (item, hideFrame) {
        if (this._highlightedItemCssClass) {
            $(item).removeClass(this._highlightedItemCssClass);
        }
        if (item._isOver && !item._isOpen) {
            item._isOver = false;
            if (hideFrame) {
                $common.setVisible(this._hoverFrame, false);
            }
        }
    },
    _breadCrumpItemHighLight: function (item) {
        if (!item._isOver && !this._isOpen) {
            if (this._highlightedItemCssClass) {
                $(item).addClass(this._highlightedItemCssClass);
            }
            item._pathParent._currentBreadCrumb = this;
            item._isOver = true;
            var bounds = $common.getBounds(item);
            //var offset = $common.getLocation(this._hoverFrame);
            //bounds.x -= offset.x;
            //bounds.y -= offset.y;
            $common.setBounds(this._hoverFrameTop, {
                x: bounds.x,
                y: bounds.y,
                width: bounds.width,
                height: 1
            });
            $common.setBounds(this._hoverFrameBottom, {
                x: bounds.x,
                y: bounds.y + bounds.height - 1,
                width: bounds.width,
                height: 1
            });
            $common.setBounds(this._hoverFrameLeft, {
                x: bounds.x,
                y: bounds.y,
                width: 1,
                height: bounds.height
            });
            $common.setBounds(this._hoverFrameRight, {
                x: bounds.x + bounds.width - 1,
                y: bounds.y,
                width: 1,
                height: bounds.height
            });
            $common.setBounds(this._hoverFrameSeparator, {
                x: bounds.x + bounds.width - this._dropArrowWidth - 3,
                y: bounds.y,
                width: 1,
                height: bounds.height
            });
            $common.setBounds(this._dropArrow, {
                x: bounds.x + bounds.width - this._dropArrowWidth - 3,
                y: bounds.y,
                width: this._dropArrowWidth,
                height: bounds.height
            });
            this._hoverFrameTop.style.backgroundColor = this._highlightBorderColor;
            this._hoverFrameRight.style.backgroundColor = this._highlightBorderColor;
            this._hoverFrameBottom.style.backgroundColor = this._highlightBorderColor;
            this._hoverFrameLeft.style.backgroundColor = this._highlightBorderColor;
            this._hoverFrameSeparator.style.backgroundColor = this._highlightBorderColor;
            item._highlighted = true;
            $common.setVisible(this._hoverFrameSeparator, true);
            $common.setVisible(this._hoverFrame, true);
        }
    },
    _onListMouseDown: function (ev) {
        /// <summary>
        /// Handler for the mousedown event on the autocomplete flyout.
        /// </summary>
        /// <param name="ev" type="Sys.UI.DomEvent" DomElement="false" mayBeNull="false" />
        /// <returns />
        var panel = this._pathParent;
        if (ev.target !== panel._completionListElement) {
            panel._flyoutHasFocus = false;
            panel.hideCompletionList();
            panel._breadCrumbRemove(panel._currentBreadCrumb._index, true, false);
            panel._breadCrumbAppend(ev.target._key, $(ev.target).text(), true);
            panel.raiseOnItemSelected(new AjaxControlToolkit.BreadCrumbItemEventArgs(panel._currentBreadCrumb, panel._currentBreadCrumb._value, panel._currentBreadCrumb._key));
            ev.stopPropagation();
        } else { // focus is still on the flyout and we do not want to hide it
            panel._flyoutHasFocus = true;
        }
        ev.preventDefault();
    },

    _onListMouseOver: function (ev) {
        /// <summary>
        /// Handler for the mouseover event on the autocomplete flyout.
        /// </summary>
        /// <param name="ev" type="Sys.UI.DomEvent" DomElement="false" mayBeNull="false" />
        /// <returns />      
        var item = ev.target;
        if (item !== this._completionListElement) {
            var children = this._completionListElement.childNodes;
            // make sure the selected index is updated
            for (var i = 0; i < children.length; ++i) {
                if (item === children[i]) {
                    this._highlightItem(item);
                    this._selectIndex = i;
                    break;
                }
            }
        }
    },
    _onListBlur: function (ev) {
        this.hideCompletionList();
    },
    _onTimerTick: function (sender, eventArgs) {
        /// <summary>
        /// Handler invoked when a timer tick occurs
        /// </summary>
        /// <param name="sender" type="Object" mayBeNull="true"/>
        /// <param name="eventArgs" type="Sys.EventArgs" mayBeNull="true" />        
        /// <returns />
        // turn off the timer until another key is pressed.
        this._timer.set_enabled(false);
        if (this._servicePath && this._serviceMethod) {
            var text = this._getCurrentBreadCrumbKey();
            // there is new content in the textbox or the textbox is empty but the min prefix length is 0
            this._currentContextKey = text;
            if (this._currentContextKey !== "") {
                if (this._cache && this._cache[text]) {
                    this._updateComplitionList(text, this._cache[text],
                    /* cacheResults */
                    false);
                    return;
                }
            }
            // Create the service parameters and optionally add the context parameter
            // (thereby determining which method signature we're expecting...)
            var params = {
                contextKey: this._currentContextKey
            };
            if (this._webRequest) {
                // abort the previous web service call if we 
                // are issuing a new one and the previous one is 
                // active.
                this._webRequest.get_executor().abort();
                this._webRequest = null;
            }
            // Invoke the web service
            this._webRequest = Sys.Net.WebServiceProxy.invoke(this.get_servicePath(), this.get_serviceMethod(), false, params, Function.createDelegate(this, this._onMethodComplete), Function.createDelegate(this, this._onMethodFailed), text);
            $common.updateFormToRefreshATDeviceBuffer();
        }
    },

    _onMethodComplete: function (result, context) {
        /// <summary>
        /// Handler invoked when the webservice call is completed.
        /// </summary>
        /// <param name="result" type="Object" DomElement="false" mayBeNull="true" />
        /// <param name="context" type="String" DomElement="false" mayBeNull="true" />
        /// <returns />
        this._webRequest = null; // clear out our saved WebRequest object    
        this._updateComplitionList(context, result,
        /* cacheResults */
        true);
    },

    _onMethodFailed: function (err, response, context) {
        /// <summary>
        /// Handler invoked when the webservice call fails, currently a noop
        /// </summary>
        /// <param name="err" type="Object" DomElement="false" mayBeNull="true" />        
        /// <param name="result" type="Object" DomElement="false" mayBeNull="true" />
        /// <param name="context" type="String" DomElement="false" mayBeNull="true" />
        /// <returns />
        // clear out our saved WebRequest object
        this._webRequest = null;
        this.hideCompletionList();
        this._isIpen = false;
    },
    _highlightItem: function (item) {
        /// <summary>
        /// Highlights the specified item
        /// </summary>
        /// <param name="item" type="Sys.UI.DomElement" DomElement="true" mayBeNull="false" />
        /// <returns />

        var children = this._completionListElement.childNodes;

        // Unselect any previously highlighted item
        for (var i = 0; i < children.length; i++) {
            var child = children[i];
            if (child._highlighted) {
                if (this._completionListItemCssClass) {
                    $(child).removeClass(this._highlightedItemCssClass);
                    $(child).addClass(this._completionListItemCssClass);
                } else {
                    if (Sys.Browser.agent === Sys.Browser.Safari) {
                        child.style.backgroundColor = 'white';
                        child.style.color = 'black';
                    } else {
                        child.style.backgroundColor = this._textBackground;
                        child.style.color = this._textColor;
                    }
                }
            }
        }

        // Highlight the new item
        if (this._highlightedItemCssClass) {
            $(item).removeClass(this._completionListItemCssClass);
            $(item).addClass(this._highlightedItemCssClass);
        } else {
            if (Sys.Browser.agent === Sys.Browser.Safari) {
                item.style.backgroundColor = 'lemonchiffon';
            } else {
                item.style.backgroundColor = 'highlight';
                item.style.color = 'highlighttext';
            }

        }
        item._highlighted = true;
    },
    _updateComplitionList: function (prefixText, completionItems, cacheResults) {
        /// <summary>
        /// Method to update the status of the autocomplete behavior
        /// </summary>
        /// <param name="prefixText" type="String" DomElement="false" mayBeNull="true" />
        /// <param name="completionItems" type="Object" DomElement="false" mayBeNull="true" />
        /// <param name="cacheResults" type="Object" DomElement="false" mayBeNull="true" />
        /// <returns />  
        if (prefixText === null) {
            prefixText = "";
        }
        if (cacheResults) {
            if (!this._cache) {
                this._cache = {};
            }
            this._cache[prefixText] = completionItems;
        }
        // If the target control loses focus or 
        // if the value in textbox has changed before the webservice returned
        // completion items we don't need to show popup 
        if (prefixText != this._getCurrentBreadCrumbKey()) {
            this._hideCompletionList();
            return;
        }
        if (completionItems && completionItems.length) {
            this._completionListElement.innerHTML = '';
            this._selectIndex = -1;
            var _firstChild = null;
            var key = null;
            var value = null;
            for (var i = 0; i < completionItems.length; i++) {
                // Create the item  
                var itemElement = document.createElement('div');
                // set the first child if it is null
                if (_firstChild === null) {
                    _firstChild = itemElement;
                }
                // Get the text/value for the item
                try {
                    var pair = Sys.Serialization.JavaScriptSerializer.deserialize('(' + completionItems[i] + ')');
                    if (pair && pair.First) {
                        key = pair.First;
                        value = pair.Second;
                    } else {
                        key = pair;
                        value = pair;
                    }
                } catch (ex) {
                    key = completionItems[i];
                    value = completionItems[i];
                }

                // Set the text/value for the item
                // ShowOnlyCurrentWordInCompletionListItem support
                itemElement.appendChild(document.createTextNode(value));
                itemElement._key = key;
                itemElement.__item = '';

                if (this._completionListItemCssClass) {
                    Sys.UI.DomElement.addCssClass(itemElement, this._completionListItemCssClass);
                } else {
                    var itemElementStyle = itemElement.style;
                    itemElementStyle.padding = '0px';
                    itemElementStyle.textAlign = 'left';
                    itemElementStyle.textOverflow = 'ellipsis';
                    if (Sys.Browser.agent === Sys.Browser.Safari) {
                        itemElementStyle.backgroundColor = 'white';
                        itemElementStyle.color = 'black';
                    } else {
                        itemElementStyle.backgroundColor = this._textBackground;
                        itemElementStyle.color = this._textColor;
                    }
                }
                this._completionListElement.appendChild(itemElement);
                $(itemElement).addClass("ajax__breadcrumb_menu_dropdown");
            }
            this._completionListElement.scrollTop = 0;

            this.showCompletionList();
            // Check if the first Row is to be selected by default and if yes highlight it and updated selectIndex.
            if (this._firstRowSelected && (_firstChild !== null)) {
                this._highlightItem(_firstChild);
                this._selectIndex = 0;
            }
        } else {
            this._hideCompletionList();
        }
    },
    //------------------------------------------------------------------------------
    //						EVENTS
    //------------------------------------------------------------------------------
    add_onItemSelected: function (handler) {
        /// <summary>
        /// Add an event handler for the populating event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().addHandler('onItemSelected', handler);
    },
    remove_onItemSelected: function (handler) {
        /// <summary>
        /// Remove an event handler from the populating event
        /// </summary>
        /// <param name="handler" type="Function" mayBeNull="false">
        /// Event handler
        /// </param>
        /// <returns />
        this.get_events().removeHandler('onItemSelected', handler);
    },
    raiseOnItemSelected: function (eventArgs) {
        /// <summary>
        /// Raise the populating event
        /// </summary>
        /// <param name="eventArgs" type="Sys.CancelEventArgs" mayBeNull="false">
        /// Event arguments for the populating event
        /// </param>
        /// <returns />
        /// <remarks>
        /// The populating event can be used to provide custom data to AutoComplete
        /// instead of using a web service.  Just cancel the event (using the
        /// CancelEventArgs) and pass your own data to the _update method.
        /// </remarks>
        if (this._currentBreadCrumb._isOver) {
            $(this._currentBreadCrumb).trigger("mouseleave");
        }
        var handler = this.get_events().getHandler('onItemSelected');
        if (handler) {
            handler(this, eventArgs);
        }
        if (this._autoPostBack && !this._doingPostBack) {
            __doPostBack(this.get_element().id, '');
            this.raiseItemOver(new AjaxControlToolkit.AutoCompleteItemEventArgs(item, item.innerText, item._value));
        }
    },
    //------------------------------------------------------------------------------
    //						PROPERTIES
    //------------------------------------------------------------------------------
    get_serviceMethod: function () {
        /// <value type="String" maybeNull="false">
        /// Web service method.
        /// </value>
        return this._serviceMethod;
    },
    set_serviceMethod: function (value) {
        if (this._serviceMethod != value) {
            this._serviceMethod = value;
            this.raisePropertyChanged('serviceMethod');
        }
    },
    _getSerializedPath: function () {
        var s = null;
        if (this._breadCrumbPath.length > 0) {
            try {
                s = Sys.Serialization.JavaScriptSerializer.serialize(this._breadCrumbPath);
            } catch (e) { }
        }
        return s;
    },
    saveClientState: function () {
        var state = {
            currentPath: this.get_currentPath(),
            readOnly: this.get_readOnly()
        };
        var val = Sys.Serialization.JavaScriptSerializer.serialize(state);
        this.set_ClientState(val);
        return val;
    },
    get_currentPath: function () {
        /// <value type="String" maybeNull="true">
        /// Web service url.
        /// </value>
        return this._currentPath;
    },
    set_currentPath: function (value) {
        var serializedValue = Sys.Serialization.JavaScriptSerializer.serialize(this._breadCrumbPath);
        if (serializedValue != value) {
            var deserializedValue = [];
            try {
                deserializedValue = Sys.Serialization.JavaScriptSerializer.deserialize('(' + value + ')');
            } catch (exc) { }
            this._breadCrumbPath = deserializedValue;
            if (this._initialized) {
                this._breadCrumbProcessPath();
                this._currentPath = this._getSerializedPath();
                this.saveClientState();
                this.raisePropertyChanged('currentPath');
            }
        }
    },
    get_servicePath: function () {
        /// <value type="String" maybeNull="true">
        /// Web service url.
        /// </value>
        return this._servicePath;
    },
    set_servicePath: function (value) {
        if (this._servicePath != value) {
            this._servicePath = value;
            this.raisePropertyChanged('servicePath');
        }
    },
    get_contextKey: function () {
        /// <value type="String" mayBeNull="true">
        /// User/page specific context provided to an optional overload of the
        /// web method described by ServiceMethod/ServicePath.  If the context
        /// key is used, it should have the same signature with an additional
        /// parameter named contextKey of type string.
        /// </value>
        return this._currentContextKey;
    },
    set_contextKey: function (value) {
        if (this._currentContextKey != value) {
            this._currentContextKey = value;
            this.raisePropertyChanged('currentContextKey');
        }
    },
    get_readOnly: function () {
        /// <value type="Bool" mayBeNull="true">
        /// It provides the functionality to non selectable breadCrumbPath
        /// </value>
        return this._readOnly;
    },
    set_readOnly: function (value) {
        if (this._readOnly != value) {
            this._readOnly = value;
            this.raisePropertyChanged('readOnly');
        }
    },
    get_minSelectableLevel: function () {
        /// <value type="Bool" mayBeNull="true">
        /// It provides the functionality to non selectable breadCrumbPath
        /// </value>
        return this._minSelectableLevel;
    },
    set_minSelectableLevel: function (value) {
        if (this._minSelectableLevel != value) {
            this._minSelectableLevel = value;
            this._breadCrumbProcessPath();
        }
    },
    get_completionListCssClass: function () {
        /// <value type="String" maybeNull="true">
        /// Css class name that will be used to style the completion list element.
        /// </value>
        return this._completionListCssClass;
    },
    set_completionListCssClass: function (value) {
        if (this._completionListCssClass != value) {
            this._completionListCssClass = value;
            this.raisePropertyChanged('completionListCssClass');
        }
    },

    get_completionListHoverCssClass: function () {
        /// <value type="String" maybeNull="true">
        /// Css class name that will be used to style the completion list element.
        /// </value>
        return this._completionListHoverCssClass;
    },
    set_completionListHoverCssClass: function (value) {
        if (this._completionListHoverCssClass != value) {
            this._completionListHoverCssClass = value;
            this.initializePanel(this._parent);
            this.raisePropertyChanged('completionListHoverCssClass');
        }
    },

    get_completionListItemCssClass: function () {
        /// <value type="String" maybeNull="true">
        /// Css class name that will be used to style an item in the completion list.
        /// </value>
        return this._completionListItemCssClass;
    },
    set_completionListItemCssClass: function (value) {
        if (this._completionListItemCssClass != value) {
            this._completionListItemCssClass = value;
            this.raisePropertyChanged('completionListItemCssClass');
        }
    },

    get_highlightedItemCssClass: function () {
        /// <value type="String" maybeNull="true">
        /// Css class name that will be used to style a highlighted item in the list.
        /// </value>
        return this._highlightedItemCssClass;
    },
    set_highlightedItemCssClass: function (value) {
        if (this._highlightedItemCssClass != value) {
            this._highlightedItemCssClass = value;
            this.raisePropertyChanged('highlightedItemCssClass');
        }
    },
    get_dropArrowBackgroundColor: function () {
        return this._dropArrowBackgroundColor;
    },
    set_dropArrowBackgroundColor: function (value) {
        if (this._dropArrowBackgroundColor != value) {
            this._dropArrowBackgroundColor = value;
            if (this.get_isInitialized()) {
                this._dropArrow.style.backgroundColor = value;
            }
            this.raisePropertyChanged("dropArrowBackgroundColor");
        }
    },
    get_dropArrowImageUrl: function () {
        return this._dropArrowImageUrl;
    },
    set_dropArrowImageUrl: function (value) {
        if (this._dropArrowImageUrl != value) {
            this._dropArrowImageUrl = value;
            if (this.get_isInitialized()) {
                if (this._dropArrow.className) {
                    this._dropArrow.className = "";
                    this._dropArrowWrapper.style.display = 'block';
                }
                this._dropArrowImage.src = value;
            }
            this.raisePropertyChanged("dropArrowImageUrl");
        }
    },
    get_dropArrowWidth: function () {
        return this._dropArrowWidth;
    },
    set_dropArrowWidth: function (value) {
        if (this._dropArrowWidth != value) {
            this._dropArrowWidth = value;
            if (this.get_isInitialized()) {
                this._dropArrow.style.width = value;
            }
            this.raisePropertyChanged("dropArrowWidth");
        }
    },
    get_autoPostBack: function () {
        /// <value type="String" maybeNull="true">
        /// Css class name that will be used to style the completion list element.
        /// </value>
        return this._autoPostBack;
    },
    set_autoPostBack: function (value) {
        if (this._autoPostBack != value) {
            this._autoPostBack = value;
            this.raisePropertyChanged('autoPostBack');
        }
    }
};
AjaxControlToolkit.BreadCrumbBehavior.descriptor = {
    properties: [{
        name: 'currentPath',
        type: String
    },
    {
        name: 'readOnly',
        type: Boolean
    },
    {
        name: 'highlightedItemCssClass',
        type: String
    },
    {
        name: 'completionListItemCssClass',
        type: String
    },
    {
        name: 'completionListHoverCssClass',
        type: String
    },
    {
        name: 'completionListCssClass',
        type: String
    },
    {
        name: 'servicePath',
        type: String
    },
    {
        name: 'serviceMethod',
        type: String
    },
    {
        name: 'dropArrowWidth',
        type: String
    },
    {
        name: 'dropArrowImageUrl',
        type: String
    },
    {
        name: 'dropArrowBackgroundColor',
        type: String
    }]
};
AjaxControlToolkit.BreadCrumbBehavior.registerClass('AjaxControlToolkit.BreadCrumbBehavior', AjaxControlToolkit.BehaviorBase);

AjaxControlToolkit.BreadCrumbItemEventArgs = function(item, text, value) {
    /// <summary>
    /// Event arguments used when the itemSelected event is raised
    /// </summary>
    /// <param name="item" type="Sys.UI.DomElement" DomElement="true" mayBeNull="true">
    /// Item
    /// </param>
    /// <param name="text" type="String" mayBeNull="true">
    /// Text of the item
    /// </param>
    /// <param name="value" type="String" mayBeNull="true" optional="true">
    /// Value of the item different from text if specifically returned by the webservice
    /// as autocomplete json text/value item(using AutoComplete.CreateAutoCompleteItem); otherwise same as text.
    /// </param>
    AjaxControlToolkit.BreadCrumbItemEventArgs.initializeBase(this);

    this._item = item;
    this._text = text;
    this._value = (value !== undefined) ? value: null;
};
AjaxControlToolkit.BreadCrumbItemEventArgs.prototype = {
    get_item: function() {
        /// <value type="Sys.UI.DomElement" DomElement="true" mayBeNull="true">
        /// Item
        /// </value>
        return this._item;
    },
    set_item: function(value) {
        this._item = value;
    },

    get_text: function() {
        /// <value type="String" maybeNull="true">
        /// Text of the item
        /// </value>
        return this._text;
    },
    set_text: function(value) {
        this._text = value;
    },

    get_value: function() {
        /// <value type="String" maybeNull="true">
        /// Value of the item different from text if specifically returned by the webservice
        /// as autocomplete json text/value item(using AutoComplete.CreateAutoCompleteItem); otherwise same as text.
        /// </value>
        return this._value;
    },
    set_value: function(value) {
        this._value = value;
    }
};
AjaxControlToolkit.BreadCrumbItemEventArgs.registerClass('AjaxControlToolkit.BreadCrumbItemEventArgs', Sys.EventArgs);